package com.logger.loader;

import com.logger.Level;
import com.logger.Logger;
import com.logger.LoggerConfig;
import com.logger.Option;
import com.logger.common.Utils;
import com.logger.handlers.AHandler;
import com.logger.handlers.HandlerManager;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;

/**
 * Properties文件加载器
 * Created by chenxunlin01 on 2016/1/16.
 */

public class PropertiesLoader extends Loader {

    private static final String PREFFIX = "log.";
    private static final String PARAM_ROOT = "root";
    private static final String PARAM_OPTION = "option";
    private static final String PARAM_HANDLER = "handler";
    private static final String PARAM_LOGGER = "logger";

    private BufferedReader reader;

    public PropertiesLoader(InputStream inputStream) {
        super(inputStream);
    }

    @Override
    public boolean loadConfig() {
        try {
            reader = new BufferedReader(new InputStreamReader(getInputStream()));
            readConfig();
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            Utils.closeIO(reader);
        }
        return false;
    }

    private void readConfig() throws Exception {
        String line = null;
        while ((line = reader.readLine()) != null) {
            line = line.trim();
            if (line.isEmpty()) {
                continue;
            }
            if (line.startsWith("#")) {
                continue;
            }
            if (!line.startsWith(PREFFIX) || line.equals(PREFFIX)) {
                throw new Exception("logger config has invalid line");
            }
            readLine(line);
        }
    }

    private void readLine(String line) throws Exception {
        String[] keyValue = readKeyValue(line);
        if (keyValue.length != 2) {
            throw new Exception("logger config has a invalid line " + line);
        }
        String key = keyValue[0].trim();
        String val = keyValue[1].trim();
        if (key.isEmpty() || val.isEmpty()) {
            throw new Exception("logger config has a invalid line:" + line);
        }
        String[] args = key.split("\\.");
        if (args.length < 2) {
            throw new Exception("logger config has a invalid line:" + line);
        }
        readParam(args[1], args, val);
    }

    /**
     * 读取每一行的配置
     *
     * @param line
     * @return
     * @throws Exception
     */
    private String[] readKeyValue(String line) throws Exception {
        int splitIndex = -1;
        if ((splitIndex = line.indexOf('=')) < 0) {
            splitIndex = line.indexOf(':');
        }
        if (splitIndex < 0) {
            throw new Exception("logger config has the invalid separate char in line:" + line);
        }
        if (splitIndex >= line.length() - 1) {
            throw new Exception("logger config has the invalid value in line:" + line);
        }
        String[] keyval = new String[2];
        keyval[0] = line.substring(0, splitIndex);
        keyval[1] = line.substring(splitIndex + 1);
        return keyval;
    }

    private void readParam(String param, String[] args, String val) throws Exception {
        LoggerConfig config = Logger.config;
        if (PARAM_OPTION.equalsIgnoreCase(param) || PARAM_LOGGER.equalsIgnoreCase(param)) {
            config.addOption(readOption(args, val));
        } else if (PARAM_HANDLER.equalsIgnoreCase(param)) {
            readHandler(args, val);
        } else if (PARAM_ROOT.equalsIgnoreCase(param)) {
            config.setRoot(readOption(args, val));
        }
    }

    private Option readOption(String[] args, String val) throws Exception {
        Option option = new Option();
        option.setName(getString(args, 2));
        String[] cfgs = val.split(",");
        String levelKey = cfgs[0].trim();
        Level level = Level.getLevel(levelKey);
        if (level == null) {
            throw new Exception("Logger level '" + levelKey + "' doesn't exists.");
        }
        option.setLevel(level);
        int len = cfgs.length;
        if (len > 1) {
            int size = len - 1;
            String[] appenders = new String[size];
            for (int i = 0; i < size; i++) {
                String appender = cfgs[i + 1].trim();
                if (appender.isEmpty()) {
                    throw new Exception("Logger category appender cannot be empty.");
                }
                appenders[i] = appender;
            }
            option.setHandlers(appenders);
        }
        return option;
    }

    private void readHandler(String[] args, String val) throws Exception {
        if (args.length == 3) {
            readNewHandler(args[2], val);
        } else if (args.length > 3) {
            readHandlerParam(args, val);
        }
    }

    private void readNewHandler(String name, String className) throws Exception {
        name = name.trim();
        if (name.isEmpty()) {
            throw new Exception("Cannot find logger appender's name, which is empty.");
        }
        Class<?> clazz = Utils.loadCLass(className);
        if (clazz == null) {
            throw new Exception("Cannot find logger appender's class '" + className + "'");
        }
        AHandler handler = (AHandler) Utils.newInstance(clazz);
        if (handler == null) {
            throw new Exception("Cannot instance class '" + clazz + "'");
        }
        handler.setName(name);
        HandlerManager.registerHandler(handler);
    }

    private void readHandlerParam(String[] args, String val) throws Exception {
        String name = args[2].trim();
        if (name.isEmpty()) {
            throw new Exception("Cannot find logger handler's name, which is empty.");
        }

        AHandler handler = HandlerManager.getHandler(name);
        if (handler == null) {
            throw new Exception("can not find handler by " + name);
        }
        Object baseObj = handler;
        int size = args.length;
        for (int i = 3; i < size - 1; i++) {
            String field = args[i].trim();
            if (field.isEmpty()) {
                throw new Exception("handler's params setting has a invalid field");
            }
            baseObj = Utils.getGetMethodObject(baseObj, args[i]);
            if (baseObj == null) {
                throw new Exception("Appender's params setting field '" + args[i] + "' is null.");
            }
        }
        name = args[size - 1];
        if (!Utils.setStringValue(baseObj, name, val)) {
            throw new Exception("Fail to set handler param value for " + baseObj.getClass() + "'s key " + name);
        }
    }

    private String getString(String[] args, int start) throws Exception {
        return getString(args, start, args.length - 1);
    }

    private String getString(String[] args, int start, int end) throws Exception {
        if (end < start) {
            return null;
        }
        StringBuilder buf = new StringBuilder();
        for (int i = start; i <= end; i++) {
            String tmp = args[i].trim();
            if (tmp.isEmpty()) {
                throw new Exception("logger config name is empty.");
            }
            buf.append(tmp);
            buf.append('.');
        }
        buf.setLength(buf.length() - 1);
        return buf.toString();
    }
}
